package com.yhsimon.nio.c4;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import static com.yhsimon.nio.c1.ByteBufferUtil.debugAll;


/**
 * @author： YHSimon
 * @date： 2022-07-12 14:30
 */
@Slf4j
public class Server {

    private static void split(ByteBuffer source) {
        source.flip();
        for (int i = 0; i < source.limit(); i++) {
            if (source.get(i) == '\n') {//读指针不会移动 position
                int len = i + 1 - source.position();
                //找到一条完整消息,并存入新ByteBuffer
                ByteBuffer target = ByteBuffer.allocate(len);
                //从source读，向target写
                for (int j = 0; j < len; j++) {
                    target.put(source.get());
                }
                debugAll(target);
            }
        }
        source.compact();//存在未读的半包，留给下一次读取
    }

    public static void main(String[] args) throws IOException {

        // 1.创建Selector 管理多个channel
        Selector selector = Selector.open();

        ByteBuffer buffer = ByteBuffer.allocate(16);
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);

        // 2. 创建selector和channel的联系(注册)
        // SelectionKey 将来事件发生后，通过它可以知道是什么类型事件和哪个Channel的事件
        SelectionKey sscKey = ssc.register(selector, 0);
        // key只关注accept事件
        sscKey.interestOps(SelectionKey.OP_ACCEPT);
        log.debug("register key:{}", sscKey);
        ssc.bind(new InetSocketAddress(8080));
        while (true) {
            // 3. select 方法  没有事件发生，线程阻塞，有事件，线程才会继续运行
            // select 在事件未处理时，它不会阻塞
            selector.select();
            // 4. 处理事件  selectedKeys包含所有发生的事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 处理key时，要从selectedKeys集合中移除，否则下次处理就会有问题
                iterator.remove();
                log.debug("key:{}", key);
                // 5. 区分事件类型
                if (key.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                    SocketChannel sc = channel.accept();
                    sc.configureBlocking(false);
                    ByteBuffer buffer1 = ByteBuffer.allocate(16); //attachment
                    // 将一个byteBuffer作为附件关联到selectionKey上 buffer1的生命周期延长到与scKey一致
                    SelectionKey scKey = sc.register(selector, 0, buffer1);
                    scKey.interestOps(SelectionKey.OP_READ);
                    log.debug("{}", sc);
                } else if (key.isReadable()) {
                    try {
                        SocketChannel channel = (SocketChannel) key.channel();
                        // 获取作为attachment的byteBuffer
                        ByteBuffer buffer2 = (ByteBuffer) key.attachment();
                        int read = channel.read(buffer2); // 如果是正常断开，read的方法返回值为-1
                        if (read == -1) {
                            key.cancel();
                        } else {
                            split(buffer2);
                            if(buffer2.position()==buffer2.limit()){//扩容条件
                                ByteBuffer newBuffer = ByteBuffer.allocate(buffer2.capacity() * 2);
                                buffer2.flip();
                                newBuffer.put(buffer2);
                                key.attach(newBuffer);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        key.cancel();//因为客户端断开，因此需要将key取消（从selector的keys集合中真正删除key）
                    }
                }

            }
        }
    }
}
